package net.protocol.diameter.avp;

import net.protocol.diameter.dictionary.AVPFormat;
import net.protocol.diameter.dictionary.AVPRule;
import net.protocol.diameter.dictionary.MessageFormat;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;

/**
 * @author jinhongw@gmail.com
 */
public class AVPFactory {

    /**
     * Creates a <tt>AVP</tt> collection with the given MessageFormat and source ByteBuffer
     *
     * @param format Diameter Message Format
     * @param src The buffer from which bytes are to be retrieved
     * @return A collection for all Diameter Message's AVPs
     */
    public static Collection<AVP<?>> createMsgAVPs(MessageFormat format, ByteBuffer src) {
        Collection<AVP<?>> avps = new ArrayList<AVP<?>>();
        while (src.hasRemaining()) {
            DiameterAVPHeader vh = new DiameterAVPHeader(src);
            AVPRule rule = format.get(vh.getVendorId(), vh.getCode());
            AVPFormat af = rule.getAVPFormat();
            AVP<?> avp = AVPFactory.createAVP(af, vh, src);
            if (avp == null) continue;
            avps.add(avp);
        }
        return avps;
    }

    /**
     *  Creates a <tt>AVP</tt> collection with the given AVPFormat and source ByteBuffer
     *
     * @param format Diameter Group AVP Format
     * @param src The buffer from which bytes are to be retrieved
     * @return A collection for all Diameter Group AVP's AVPs
     *
     * @throws IllegalArgumentException if this format be Non-Group AVP Format
     */
    public static Collection<AVP<?>> createGroupAVPs(AVPFormat format, ByteBuffer src) {
        if (!format.isGroup())
            throw new IllegalArgumentException("The given format must be Group AVP Format");

		final Collection<AVP<?>> avps = new ArrayList<AVP<?>>();
        while (src.hasRemaining()) {
            DiameterAVPHeader vh = new DiameterAVPHeader(src);
            AVPRule rule = format.get(vh.getVendorId(), vh.getCode());
            AVP<?> avp = AVPFactory.createAVP(rule.getAVPFormat(), vh, src);
            if (avp == null) continue;
            avps.add(avp);
        }
		return avps;
    }

    /**
     * @param format Diameter AVP Format
     * @param header Diameter AVP Header
     * @param src The buffer from which bytes are to be retrieved
     * @return A new <code>AVP</code>
     */
    public static AVP<?> createAVP(AVPFormat format, DiameterAVPHeader header, ByteBuffer src) {
        switch (format.getDataFormat()) {
            case OctetString:
                return new OctetString(header, src);
            case DiameterIdentity:
                return new DiameterIdentity(header, src);
            case UTF8String:
                return new UTF8String(header, src);
            case Integer32:
                return new Integer32(header, src);
            case Enumerated:
                return new Enumerated(header, src);
            case Integer64:
                return new Integer64(header, src);
            case Unsigned32:
                return new Unsigned32(header, src);
            case Float32:
                return new Float32(header, src);
            case Float64:
                return new Float64(header, src);
            case Time:
                return new Time(header, src);
            case Address:
                return new Address(header, src);
            case Unsigned64:
                return new Unsigned64(header, src);
            case Grouped:
                return new Grouped(format, header, src);
            case DiameterURI:
                return new DiameterURI(header, src);
            case IPFilterRule:
                return new IPFilterRule(header, src);
            case QoSFilterRule:
                return new QoSFilterRule(header, src);
            default:
                return null;
        }
    }
}
